Microsoft DirectX 8.1 (C++)

Using Windowless Mode with the VMR

This topic applies to Windows XP Home Edition and Windows XP Professional only.

In Windows XP Home Edition and Windows XP Professional, the new Video Mixing Renderer Filter (VMR) is the default video renderer. The VMR supports many powerful features, including alpha blending of multiple video streams, but is also completely backward-compatible with existing applications.

This section describes a new rendering mode, called windowless mode, that represents an improvement over the IVideoWindow interface. For more information about the VMR, see Using the Video Mixing Renderer.

Windowed Mode versus Windowless Mode

The older Video Renderer Filter creates its own window to display the video. If you want to show the video inside an application-defined window, you must set the video window as a child of the application window, using the IVideoWindow interface on the Filter Graph Manager. However, the existence of a separate video window causes some problems. Most importantly, there is a potential for deadlocks if inter-thread messages are sent inadvertently. Other drawbacks to windowed mode include the following:

To avoid these problems, the VMR supports drawing directly on the client area, using DirectDraw to draw the video and clip the video rectangle. This mode is called "windowless" because it does not use a child video window. Windowless mode significantly reduces the chance of deadlocks. The application does not have to set the owner window or the window styles. In fact, when the VMR is in windowless mode, it does not expose the IVideoWindow and IBasicVideo2 interfaces, because they are unnecessary.

To remain backward-compatible with existing applications, the VMR defaults to windowed mode. From the application's perspective, this mode behaves just like the old Video Renderer filter. Applications using windowless mode must explicitly configure the VMR. You will find, however, that windowless mode is more flexible and easier to use.

Configuring the VMR for Windowless Mode

To override the VMR's default behavior, you must configure the VMR for windowless mode before you build the filter graph. To do so, perform the following steps:

  1. Create the Filter Graph Manager.
  2. Create the VMR and add it to the filter graph.
  3. Set the mode by calling IVMRFilterConfig::SetRenderingMode with the value VMRMode_Windowless.
  4. Call IVMRWindowlessControl::SetVideoClippingWindow with a handle to the window where you want the video to appear.

Now you can build the rest of the filter graph by calling IGraphBuilder::RenderFile or other graph-building methods. The Filter Graph Manager automatically uses the instance of the VMR that you added to the graph. (For details on why this happens, see Intelligent Connect.)

The following code shows a helper function that creates the VMR, adds it to the graph, and sets up windowless mode. This example assumes that you want to display only one stream, and that you are not mixing a static bitmap onto the video. To do either of these things, call the IVMRFilterConfig::SetNumberOfStreams method. For details, see Configuring For Windowless Mode.

HRESULT InitWindowlessVMR( 
    HWND hwndApp,                  // Window to hold the video. 
    IGraphBuilder* pGraph,         // Pointer to the Filter Graph Manager. 
    IVMRWindowlessControl** ppWc,  // Receives a pointer to the VMR.
    ) 
{ 
    IBaseFilter* pVmr = NULL; 
    IVMRWindowlessControl* pWc = NULL; 
    *ppWc = NULL; 

    // Create the VMR. 
    HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, 
                     CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr); 
    if (SUCCEEDED(hr)) 
    { 
        // Add the VMR to the filter graph.
        hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer"); 
        if (SUCCEEDED(hr)) 
        { 
            IVMRFilterConfig* pConfig; 
            hr = pVmr->QueryInterface(__uuidof(IVMRFilterConfig), 
                        (void**)&pConfig); 
            if( SUCCEEDED(hr)) 
            { 
                // Set the rendering mode.  
                hr = pConfig->SetRenderingMode(VMRMode_Windowless); 
                pConfig->Release(); 
            } 
            hr = pVmr->QueryInterface(__uuidof(IVMRWindowlessControl), 
                    (void**)&pWc); 
            if( SUCCEEDED(hr)) 
            { 
                // Set the window. 
                hr = pWc->SetVideoClippingWindow(hwndApp); 
                // Return an AddRef'd pointer. 
                *ppWc = pWc; 
            } 
        } 
        pVmr->Release(); 
    } 
    return hr; 
} 

The following code example uses the previous function to build a graph for file playback:

IVMRWindowlessControl *pWc = NULL;
IGraphBuilder *pGraph = NULL;
WCHAR g_wszVideoFile[] = L"C:\\Example.avi"
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
                      IID_IGraphBuilder, (void**)&pGraph);
if (SUCCEEDED(hr))
{
    hr = InitWindowlessVMR(hwnd, pGraph, &g_pWc);
    if (SUCCEEDED(hr))
    {
        hr = g_pGraph->RenderFile(g_wszVideoFile, NULL);
    }
}

Positioning the Video

After you configure the VMR, you must set the position of the video. This involves two rectangles: the source rectangle and the destination rectangle. The source rectangle specifies the portion of the video to display. The destination rectangle specifies a region within the window's client area to receive the video. The VMR crops the video image to the source rectangle, and stretches the source rectangle to fill the destination rectangle.

To set the position, call the IVMRWindowlessControl::SetVideoPosition method. You can also use the IVMRWindowlessControl::GetNativeVideoSize method to get the native size of the video. The source rectangle must be equal to or smaller than the native video size.

The following example sets the position to include the upper-left quadrant of the video image, displayed in the upper-left corner of the window:

void SetVideoPosition(HWND hwnd, IVMRWindowlessControl *pWc) 
{ 
    RECT rcSrc, rcDest; // Source and destination rectangles.
    long lWidth, lHeight; 

    g_pWc->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL); 
    SetRect(&rcSrc, 0, 0, lWidth/2, lHeight/2); 
    GetClientRect(hwnd, &rcDest); 
    SetRect(&rcDest, 0, 0, rcDest.right/2, rcDest.bottom/2); 

    g_pWc->SetVideoPosition(&rcSrc, &rcDest); 
}

Handling Window Messages

Because the VMR does not use its own window, the application must inform the VMR when it receives either a WM_PAINT message or a WM_DISPLAYCHANGE message. On WM_PAINT, call IVMRWindowlessControl::RepaintVideo to repaint the image. On WM_DISPLAYCHANGE, call IVMRWindowlessControl::DisplayModeChanged. This method enables the VMR to take any actions that are needed to display the video at the new resolution or color depth. Also, on a WM_SIZE message the application should recalculate the position of the video and call SetVideoPosition again if necessary.

The following example shows a WM_PAINT message handler. It paints a region defined by the client rectangle minus the video rectangle. Do not draw onto the video rectangle, because the VMR will paint over it, causing flickering. For the same reason, do not set a background brush in your window class.

void OnPaint(HWND hwnd) 
{ 
    PAINTSTRUCT ps; 
    HDC         hdc; 
    RECT        rcClient; 
    GetClientRect(hwnd, &rcClient); 
    hdc = BeginPaint(hwnd, &ps); 
    if(g_pWc) 
    { 
        HRGN rgnClient = CreateRectRgnIndirect(&rcClient); 
        HRGN rgnVideo  = CreateRectRgnIndirect(&g_rcDest);  // Saved from earlier.
        CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF);  // Paint on this region.

        HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE); 
        FillRgn(hdc, rgnClient, hbr); 
        DeleteObject(hbr); 
        DeleteObject(rgnClient); 
        DeleteObject(rgnVideo); 

        // Request the VMR to paint the video.
        HRESULT hr = g_pWc->RepaintVideo(hwnd, hdc);  
    } 
    else  // No video image. Just paint the whole client area. 
    { 
        FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1)); 
    } 
    EndPaint(hwnd, &ps); 
} 

Although you must respond to WM_PAINT messages, there is nothing you need to do between WM_PAINT messages to update the video.

As this example shows, windowless mode lets you treat the video image simply as a self-drawing region on the window. The VMRPlayer Sample demonstrates using windowless mode in a full featured UI, with controls, status windows, and so forth. It also demonstrates mixing two video streams.